﻿using System;
using System.IO;

namespace ooWii.IO
{
	public class GCRootDirectoryInfo : GCDirectoryInfo
	{
		public GCRootDirectoryInfo()
			: base( "", null )
		{
		}

		public void Load( Stream partition, long fstOffset, UInt32 fstSize )
		{
			byte[] fstBuffer = new byte[ fstSize * 4 ];

			partition.Position = fstOffset;
			if( partition.Read( fstBuffer, 0, fstBuffer.Length ) != fstBuffer.Length )
			{
				throw new GCException( "Cannot read file system" );
			}

			if( fstBuffer[ 0 ] != 1 || fstBuffer[ 1 ] != 0 || fstBuffer[ 2 ] != 0 || fstBuffer[ 3 ] != 0 ||
				WiiBuffer.GetUInt32( fstBuffer, 4 ) != 0 )
			{
				throw new GCException( "Invalid root directory" );
			}

			UInt32 fstEntries = WiiBuffer.GetUInt32( fstBuffer, 8 );

			processDirectory( this, partition, fstEntries * 12, fstBuffer, 0, fstEntries );
		}

		int processDirectory( GCDirectoryInfo currentDirectory, Stream partition, UInt32 stringTableOffset, byte[] fstBuffer, int currentDirectoryOffset, UInt32 fstEntries )
		{
			int fstEntry = currentDirectoryOffset;
			fstEntry++;

			while( fstEntry < fstEntries )
			{
				int fstIndex = fstEntry * 12;

				byte flags = fstBuffer[ fstIndex + 0 ];
				UInt32 fileNameOffset = ( (UInt32)fstBuffer[ fstIndex + 1 ] << 16 ) | ( (UInt32)fstBuffer[ fstIndex + 2 ] << 8 ) | fstBuffer[ fstIndex + 3 ];

				int stringStart = (int)( stringTableOffset + fileNameOffset );
				int stringLength = fstBuffer.Length - stringStart;

				if( stringLength < 1 ||
					( fileNameOffset > 0 && fstBuffer[ stringStart - 1 ] != 0 ) )
				{
					throw new GCException( "Invalid file name offset" );
				}

				string name = WiiBuffer.GetString( fstBuffer, stringStart, stringLength );

				switch( flags )
				{
				case 0:
					long fileOffset = WiiBuffer.GetUInt34( fstBuffer, fstIndex + 4 );
					UInt32 fileSize = WiiBuffer.GetUInt32( fstBuffer, fstIndex + 8 );
					currentDirectory.AddFile( new GCFileInfo( partition, currentDirectory, name, fileOffset, fileSize ) );
					fstEntry++;
					break;

				case 1:
					UInt32 parentOffset = WiiBuffer.GetUInt32( fstBuffer, fstIndex + 4 );
					UInt32 nextDirectory = WiiBuffer.GetUInt32( fstBuffer, fstIndex + 8 );

					if( parentOffset != currentDirectoryOffset )
					{
						throw new GCException( "Invalid parent offset" );
					}

					GCDirectoryInfo subDirectory = new GCDirectoryInfo( name, currentDirectory );
					currentDirectory.AddDirectory( subDirectory );

					fstEntry = processDirectory( subDirectory, partition, stringTableOffset, fstBuffer, fstEntry, nextDirectory );
					break;

				default:
					throw new GCException( "Invalid directory entry type" );
				}
			}

			return fstEntry;
		}
	}
}
